﻿using SqlSugar;
using SqlsugarService.Application.DTOs.Process;
using SqlsugarService.Application.IService.Process.BomManagement;
using SqlsugarService.Application.Until;
using SqlsugarService.Domain.BOM;
using SqlsugarService.Domain.Craftsmanship;
using SqlsugarService.Domain.Materials;
using SqlsugarService.Infrastructure.DbContext;
using SqlsugarService.Infrastructure.IRepository;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace SqlsugarService.Application.Service.Process.BomManagement
{
    /// <summary>
    /// BOM管理服务实现类
    /// 负责处理BOM（物料清单）的业务逻辑
    /// </summary>
    public class BomService : IBomService
    {
        #region 依赖注入

        private readonly SqlSugarDbContext _dbContext;
        private readonly IBaseRepository<BomInfo> _bomRepository;
        private readonly IBaseRepository<BomItem> _bomItemRepository;
        private readonly IBaseRepository<ProcessRouteEntity> _processRouteRepository;

        public BomService(
            SqlSugarDbContext dbContext,
            IBaseRepository<BomInfo> bomRepository,
            IBaseRepository<BomItem> bomItemRepository,
            IBaseRepository<ProcessRouteEntity> processRouteRepository)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _bomRepository = bomRepository ?? throw new ArgumentNullException(nameof(bomRepository));
            _bomItemRepository = bomItemRepository ?? throw new ArgumentNullException(nameof(bomItemRepository));
            _processRouteRepository = processRouteRepository ?? throw new ArgumentNullException(nameof(processRouteRepository));
        }

        #endregion

        #region Bom流程

        /// <summary>
        /// 根据BOM ID获取工艺路线ID、名称
        /// </summary>
        /// <param name="bomId">BOM ID</param>
        /// <returns>工艺路线信息</returns>
        public async Task<ApiResult<GetRoutingDto>> GetRoutingByBomIdAsync(Guid bomId)
        {
            try
            {
                // 根据BOM ID查询BOM信息，获取工艺路线ID
                var bom = await _dbContext.Db.Queryable<BomInfo>()
                    .Where(b => b.Id == bomId)
                    .FirstAsync();

                if (bom == null)
                {
                    return ApiResult<GetRoutingDto>.Fail("BOM不存在", ResultCode.NotFound);
                }

                // 根据工艺路线ID查询工艺路线信息
                var processRoute = await _dbContext.Db.Queryable<ProcessRouteEntity>()
                    .Where(pr => pr.Id == bom.ProcessRouteId)
                    .FirstAsync();

                if (processRoute == null)
                {
                    return ApiResult<GetRoutingDto>.Fail("工艺路线不存在", ResultCode.NotFound);
                }

                var result = new GetRoutingDto
                {
                    Id = processRoute.Id,
                    ProcessRouteName = processRoute.ProcessRouteName ?? string.Empty,
                    ProcessRouteNumber = processRoute.ProcessRouteNumber ?? string.Empty
                };

                return ApiResult<GetRoutingDto>.Success(result, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<GetRoutingDto>.Fail($"获取工艺路线失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 根据工艺路线ID获取工序ID、名称列表
        /// </summary>
        /// <param name="processRouteId">工艺路线ID</param>
        /// <returns>工序信息列表</returns>
        public async Task<ApiResult<List<GetProcessDto>>> GetProcessesByRouteIdAsync(Guid processRouteId)
        {
            try
            {
                // 验证工艺路线是否存在
                var processRoute = await _dbContext.Db.Queryable<ProcessRouteEntity>()
                    .Where(pr => pr.Id == processRouteId)
                    .FirstAsync();

                if (processRoute == null)
                {
                    return ApiResult<List<GetProcessDto>>.Fail("工艺路线不存在", ResultCode.NotFound);
                }

                // 通过工艺路线-工序关联表查询工序信息
                var processes = await _dbContext.Db.Queryable<ProcessRouteStep>()
                    .LeftJoin<ProcessStep>((prs, ps) => prs.ProcessStepId == ps.Id)
                    .Where((prs, ps) => prs.ProcessRouteId == processRouteId && prs.IsActive)
                    .OrderBy(prs => prs.StepOrder)
                    .Select((prs, ps) => new GetProcessDto
                    {
                        Id = ps.Id,
                        ProcessStepName = ps.ProcessStepName ?? string.Empty
                    })
                    .ToListAsync();

                return ApiResult<List<GetProcessDto>>.Success(processes, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<List<GetProcessDto>>.Fail($"获取工序列表失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 根据工序ID获取当前物料清单
        /// </summary>
        /// <param name="processStepId">工序ID</param>
        /// <returns>物料清单列表</returns>
        public async Task<ApiResult<List<GetBillOfMaterialsDto>>> GetMaterialsByProcessStepIdAsync(Guid processStepId)
        {
            try
            {
                // 验证工序是否存在
                var processStep = await _dbContext.Db.Queryable<ProcessStep>()
                    .Where(ps => ps.Id == processStepId)
                    .FirstAsync();

                if (processStep == null)
                {
                    return ApiResult<List<GetBillOfMaterialsDto>>.Fail("工序不存在", ResultCode.NotFound);
                }

                // 通过工序物料详情表查询物料信息
                var materials = await _dbContext.Db.Queryable<ProcessStepMaterialDetail>()
                    .LeftJoin<MaterialEntity>((psmd, m) => psmd.MaterialId == m.Id)
                    .Where((psmd, m) => psmd.ProcessStepId == processStepId && psmd.IsActive && psmd.IsEnabled)
                    .OrderBy(psmd => psmd.SequenceNumber)
                    .Select((psmd, m) => new GetBillOfMaterialsDto
                    {
                        Id = m.Id,
                        MaterialNumber = m.MaterialNumber,
                        MaterialName = m.MaterialName,
                        SpecificationModel = m.SpecificationModel,
                        Unit = m.Unit,
                        MaterialType = m.MaterialType,
                        MaterialProperty = m.MaterialProperty,
                        Status = m.Status,
                        PurchasePrice = m.PurchasePrice,
                        SalesPrice = m.SalesPrice,
                        Remarks = m.Remarks
                    })
                    .ToListAsync();

                return ApiResult<List<GetBillOfMaterialsDto>>.Success(materials, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<List<GetBillOfMaterialsDto>>.Fail($"获取物料清单失败: {ex.Message}", ResultCode.Error);
            }
        }

        #endregion

        #region Bom => 物料

        /// <summary>
        /// 根据BOM ID获取所有相关物料清单
        /// 通过调用上面三个方法实现：BOM -> 工艺路线 -> 工序 -> 物料
        /// </summary>
        /// <param name="bomId">BOM ID</param>
        /// <returns>所有相关物料清单</returns>
        public async Task<ApiResult<List<GetBillOfMaterialsDto>>> GetAllMaterialsByBomIdAsync(Guid bomId)
        {
            try
            {
                var allMaterials = new List<GetBillOfMaterialsDto>();

                // 第一步：根据BOM ID获取工艺路线信息
                var routingResult = await GetRoutingByBomIdAsync(bomId);
                if (!routingResult.IsSuc || routingResult.Data == null)
                {
                    return ApiResult<List<GetBillOfMaterialsDto>>.Fail(
                        routingResult.Msg ?? "获取工艺路线失败", routingResult.Code);
                }

                // 第二步：根据工艺路线ID获取所有工序
                var processesResult = await GetProcessesByRouteIdAsync(routingResult.Data.Id);
                if (!processesResult.IsSuc || processesResult.Data == null)
                {
                    return ApiResult<List<GetBillOfMaterialsDto>>.Fail(
                        processesResult.Msg ?? "获取工序列表失败", processesResult.Code);
                }

                // 第三步：遍历每个工序，获取对应的物料清单
                foreach (var process in processesResult.Data)
                {
                    var materialsResult = await GetMaterialsByProcessStepIdAsync(process.Id);
                    if (materialsResult.IsSuc && materialsResult.Data != null)
                    {
                        allMaterials.AddRange(materialsResult.Data);
                    }
                }

                // 去重处理：根据物料ID去重，避免同一物料在多个工序中重复出现
                var uniqueMaterials = allMaterials
                    .GroupBy(m => m.Id)
                    .Select(g => g.First())
                    .OrderBy(m => m.MaterialNumber)
                    .ToList();

                return ApiResult<List<GetBillOfMaterialsDto>>.Success(uniqueMaterials, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<List<GetBillOfMaterialsDto>>.Fail(
                    $"获取BOM相关物料清单失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 根据BOM名称获取所有相关物料清单
        /// </summary>
        /// <param name="BomName"></param>
        /// <returns></returns>
        public async Task<ApiResult<List<GetBillOfMaterialsDto>>> GetAllMaterialsByBomNameAsync(string BomName)
        {
            try
            {
                var query = await _bomRepository.AsQueryable().FirstAsync(x => x.ProductName == BomName);

                if(query == null)
                {
                    return ApiResult<List<GetBillOfMaterialsDto>>.Fail("BOM不存在", ResultCode.NotFound);
                }

                var result = await GetAllMaterialsByBomIdAsync(query.Id);
                return ApiResult<List<GetBillOfMaterialsDto>>.Success(result.Data, ResultCode.Success);
            }
            catch (Exception)
            {

                throw;
            }
        }

        #endregion

        #region BOM管理

        /// <summary>
        /// 新增BOM - 使用事务确保数据一致性
        /// 
        /// 事务处理的重要性：
        /// 1. 确保BOM主表和明细表数据的一致性
        /// 2. 如果任何一个步骤失败，整个操作都会回滚
        /// 3. 避免出现只有主表没有明细，或只有明细没有主表的情况
        /// </summary>
        /// <param name="dto">BOM数据传输对象</param>
        /// <returns>操作结果</returns>
        public async Task<ApiResult> AddBomAsync(BomDto dto)
        {
            // 使用SqlSugar的Ado事务API开始数据库事务
            // Ado.BeginTranAsync()创建一个数据库事务，确保所有操作要么全部成功，要么全部失败
            await _dbContext.Db.Ado.BeginTranAsync();

            try
            {
                // 步骤1：数据验证 - 在事务外进行，避免不必要的事务开销
                var validationResult = await ValidateBomDto(dto);
                if (!validationResult.IsSuc)
                {
                    // 验证失败时回滚事务
                    await _dbContext.Db.Ado.RollbackTranAsync();
                    return validationResult;
                }

                // 步骤2：验证BOM编号是否重复
                // 在事务内查询，确保数据的一致性（避免并发情况下的重复插入）
                var existingBom = await _dbContext.Db.Queryable<BomInfo>()
                    .Where(b => b.BomNumber == dto.BomNumber)
                    .FirstAsync();

                if (existingBom != null)
                {
                    await _dbContext.Db.Ado.RollbackTranAsync();
                    return ApiResult.Fail($"BOM编号 {dto.BomNumber} 已存在", ResultCode.AlreadyExists);
                }

                // 步骤3：验证工艺路线是否存在
                var processRoute = await _dbContext.Db.Queryable<ProcessRouteEntity>()
                    .Where(pr => pr.Id == dto.ProcessRouteId)
                    .FirstAsync();

                if (processRoute == null)
                {
                    await _dbContext.Db.Ado.RollbackTranAsync();
                    return ApiResult.Fail("指定的工艺路线不存在", ResultCode.NotFound);
                }

                // 步骤4：创建BOM实体对象
                var bomInfo = new BomInfo
                {
                    Id = Guid.NewGuid(),
                    BomNumber = dto.BomNumber.Trim(),
                    IsSystemNumber = dto.IsSystemNumber,
                    IsDefault = dto.IsDefault,
                    Version = dto.Version.Trim(),
                    ProductId = dto.ProductId,
                    ProductName = dto.ProductName.Trim(),
                    ColorCode = dto.ColorCode?.Trim(),
                    Unit = dto.Unit?.Trim(),
                    DailyOutput = dto.DailyOutput,
                    Remark = dto.Remark?.Trim(),
                    ProcessRouteId = dto.ProcessRouteId,
                    CreatedTime = DateTime.Now,
                    LastUpdatedTime = DateTime.Now,
                    CreatedAt = DateTime.Now,
                    CreatedBy = "system"
                };

                // 步骤5：在事务中保存BOM主表
                // 使用事务内的数据库连接进行插入操作
                var insertResult = await _dbContext.Db.Insertable(bomInfo).ExecuteCommandAsync();
                if (insertResult <= 0)
                {
                    await _dbContext.Db.Ado.RollbackTranAsync();
                    return ApiResult.Fail("保存BOM失败", ResultCode.Error);
                }

                // 步骤6：如果有BOM明细，则在同一事务中批量添加
                if (dto.BomItems != null && dto.BomItems.Any())
                {
                    // 验证所有物料是否存在
                    var materialIds = dto.BomItems.Select(x => x.MaterialId).Distinct().ToList();
                    var materials = await _dbContext.Db.Queryable<MaterialEntity>()
                        .Where(m => materialIds.Contains(m.Id))
                        .ToListAsync();

                    if (materials.Count != materialIds.Count)
                    {
                        await _dbContext.Db.Ado.RollbackTranAsync();
                        return ApiResult.Fail("存在不存在的物料", ResultCode.NotFound);
                    }

                    // 检查是否有重复的物料
                    var duplicateMaterials = dto.BomItems
                        .GroupBy(x => x.MaterialId)
                        .Where(g => g.Count() > 1)
                        .Select(g => g.Key)
                        .ToList();

                    if (duplicateMaterials.Any())
                    {
                        await _dbContext.Db.Ado.RollbackTranAsync();
                        return ApiResult.Fail("BOM明细中存在重复的物料", ResultCode.ValidationError);
                    }

                    // 创建BOM明细列表
                    var bomItems = dto.BomItems.Select(itemDto => new BomItem
                    {
                        Id = Guid.NewGuid(),
                        BomId = bomInfo.Id,
                        MaterialId = itemDto.MaterialId,
                        ParentItemId = itemDto.ParentItemId,
                        Quantity = itemDto.Quantity,
                        LossRate = itemDto.LossRate,
                        InOutType = itemDto.InOutType,
                        Unit = itemDto.Unit?.Trim(),
                        Remark = itemDto.Remark?.Trim(),
                        CreatedAt = DateTime.Now,
                        CreatedBy = "system"
                    }).ToList();

                    // 在事务中批量插入BOM明细
                    var itemsInsertResult = await _dbContext.Db.Insertable(bomItems).ExecuteCommandAsync();
                    if (itemsInsertResult <= 0)
                    {
                        await _dbContext.Db.Ado.RollbackTranAsync();
                        return ApiResult.Fail("保存BOM明细失败", ResultCode.Error);
                    }
                }

                // 步骤7：提交事务
                // 如果所有操作都成功，提交事务使所有更改永久生效
                await _dbContext.Db.Ado.CommitTranAsync();

                return ApiResult.Success(ResultCode.Success);
            }
            catch (Exception ex)
            {
                // 步骤8：异常处理 - 回滚事务
                // 如果发生任何异常，回滚事务确保数据库状态不被破坏
                try
                {
                    await _dbContext.Db.Ado.RollbackTranAsync();
                }
                catch (Exception rollbackEx)
                {
                    // 记录回滚异常，但不抛出，避免掩盖原始异常
                    // 实际项目中应该使用日志框架记录这个异常
                    Console.WriteLine($"事务回滚失败: {rollbackEx.Message}");
                }

                return ApiResult.Fail($"添加BOM失败: {ex.Message}", ResultCode.Error);
            }
            finally
            {
                // 步骤9：清理资源
                // 确保事务对象被正确释放，避免资源泄漏
                _dbContext.Db.Ado.Dispose();
            }
        }

        /// <summary>
        /// 分页获取BOM列表
        /// </summary>
        /// <param name="productName">产品名称过滤</param>
        /// <param name="bomNumber">BOM编号过滤</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页大小</param>
        /// <returns>分页的BOM列表</returns>
        public async Task<ApiResult<PageResult<List<BomListDto>>>> GetBomPagedAsync(
            string? productName, string? bomNumber, int pageIndex, int pageSize)
        {
            try
            {
                // 参数验证
                if (pageIndex < 1)
                {
                    return ApiResult<PageResult<List<BomListDto>>>.Fail(
                        "页码必须大于0", ResultCode.ValidationError);
                }

                if (pageSize < 1 || pageSize > 1000)
                {
                    return ApiResult<PageResult<List<BomListDto>>>.Fail(
                        "每页大小必须在1-1000之间", ResultCode.ValidationError);
                }

                // 构建查询
                var query = _dbContext.Db.Queryable<BomInfo>();

                // 添加过滤条件
                if (!string.IsNullOrWhiteSpace(productName))
                {
                    query = query.Where(b => b.ProductName.Contains(productName.Trim()));
                }

                if (!string.IsNullOrWhiteSpace(bomNumber))
                {
                    query = query.Where(b => b.BomNumber.Contains(bomNumber.Trim()));
                }

                // 获取总数
                var totalCount = await query.CountAsync();

                // 分页查询
                var boms = await query
                    .OrderByDescending(b => b.CreatedTime)
                    .Skip((pageIndex - 1) * pageSize)
                    .Take(pageSize)
                    .ToListAsync();

                // 获取工艺路线信息和明细数量
                var bomIds = boms.Select(b => b.Id).ToList();
                var processRouteIds = boms.Select(b => b.ProcessRouteId).ToList();

                var processRoutes = await _dbContext.Db.Queryable<ProcessRouteEntity>()
                    .Where(pr => processRouteIds.Contains(pr.Id))
                    .ToListAsync();

                var itemCounts = await _dbContext.Db.Queryable<BomItem>()
                    .Where(bi => bomIds.Contains(bi.BomId))
                    .GroupBy(bi => bi.BomId)
                    .Select(g => new { BomId = g.BomId, Count = SqlFunc.AggregateCount(g.BomId) })
                    .ToListAsync();

                var processRouteDict = processRoutes.ToDictionary(pr => pr.Id, pr => pr.ProcessRouteName ?? string.Empty);
                var itemCountDict = itemCounts.ToDictionary(ic => ic.BomId, ic => ic.Count);

                // 映射到DTO
                var data = boms.Select(bom => new BomListDto
                {
                    Id = bom.Id,
                    BomNumber = bom.BomNumber ?? string.Empty,
                    IsSystemNumber = bom.IsSystemNumber,
                    IsDefault = bom.IsDefault,
                    Version = bom.Version ?? string.Empty,
                    ProductId = bom.ProductId,
                    ProductName = bom.ProductName ?? string.Empty,
                    ColorCode = bom.ColorCode,
                    Unit = bom.Unit,
                    DailyOutput = bom.DailyOutput,
                    Remark = bom.Remark,
                    ProcessRouteId = bom.ProcessRouteId,
                    ProcessRouteName = processRouteDict.GetValueOrDefault(bom.ProcessRouteId, string.Empty),
                    CreatedTime = bom.CreatedTime,
                    LastUpdatedTime = bom.LastUpdatedTime,
                    ItemCount = itemCountDict.GetValueOrDefault(bom.Id, 0)
                }).ToList();

                var pageResult = new PageResult<List<BomListDto>>
                {
                    Data = data,
                    TotalCount = totalCount,
                    TotalPage = (int)Math.Ceiling(totalCount * 1.0 / pageSize)
                };

                return ApiResult<PageResult<List<BomListDto>>>.Success(pageResult, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<PageResult<List<BomListDto>>>.Fail(
                    $"获取BOM列表失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 根据ID获取BOM详情
        /// </summary>
        /// <param name="bomId">BOM ID</param>
        /// <returns>BOM详情</returns>
        public async Task<ApiResult<BomListDto>> GetBomByIdAsync(Guid bomId)
        {
            try
            {
                var bom = await _bomRepository.GetByIdAsync(bomId);
                if (bom == null)
                {
                    return ApiResult<BomListDto>.Fail("BOM不存在", ResultCode.NotFound);
                }

                // 获取工艺路线信息
                var processRoute = await _processRouteRepository.GetByIdAsync(bom.ProcessRouteId);

                // 获取明细数量
                var itemCount = await _dbContext.Db.Queryable<BomItem>()
                    .Where(bi => bi.BomId == bomId)
                    .CountAsync();

                var result = new BomListDto
                {
                    Id = bom.Id,
                    BomNumber = bom.BomNumber ?? string.Empty,
                    IsSystemNumber = bom.IsSystemNumber,
                    IsDefault = bom.IsDefault,
                    Version = bom.Version ?? string.Empty,
                    ProductId = bom.ProductId,
                    ProductName = bom.ProductName ?? string.Empty,
                    ColorCode = bom.ColorCode,
                    Unit = bom.Unit,
                    DailyOutput = bom.DailyOutput,
                    Remark = bom.Remark,
                    ProcessRouteId = bom.ProcessRouteId,
                    ProcessRouteName = processRoute?.ProcessRouteName ?? string.Empty,
                    CreatedTime = bom.CreatedTime,
                    LastUpdatedTime = bom.LastUpdatedTime,
                    ItemCount = itemCount
                };

                return ApiResult<BomListDto>.Success(result, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<BomListDto>.Fail($"获取BOM详情失败: {ex.Message}", ResultCode.Error);
            }
        }

        #endregion

        #region BOM明细管理

        /// <summary>
        /// 为BOM添加物料明细
        /// </summary>
        /// <param name="bomId">BOM ID</param>
        /// <param name="itemDto">物料明细数据</param>
        /// <returns>操作结果</returns>
        public async Task<ApiResult> AddBomItemAsync(Guid bomId, BomItemDto itemDto)
        {
            try
            {
                // 验证BOM是否存在
                var bom = await _bomRepository.GetByIdAsync(bomId);
                if (bom == null)
                {
                    return ApiResult.Fail("BOM不存在", ResultCode.NotFound);
                }

                // 验证物料是否存在
                var material = await _dbContext.Db.Queryable<MaterialEntity>()
                    .Where(m => m.Id == itemDto.MaterialId)
                    .FirstAsync();

                if (material == null)
                {
                    return ApiResult.Fail("指定的物料不存在", ResultCode.NotFound);
                }

                // 验证是否重复
                var existingItem = await _dbContext.Db.Queryable<BomItem>()
                    .Where(bi => bi.BomId == bomId && bi.MaterialId == itemDto.MaterialId)
                    .FirstAsync();

                if (existingItem != null)
                {
                    return ApiResult.Fail("该物料已存在于BOM中", ResultCode.AlreadyExists);
                }

                // 创建BOM明细
                var bomItem = new BomItem
                {
                    Id = Guid.NewGuid(),
                    BomId = bomId,
                    MaterialId = itemDto.MaterialId,
                    ParentItemId = itemDto.ParentItemId,
                    Quantity = itemDto.Quantity,
                    LossRate = itemDto.LossRate,
                    InOutType = itemDto.InOutType,
                    Unit = itemDto.Unit?.Trim(),
                    Remark = itemDto.Remark?.Trim(),
                    CreatedAt = DateTime.Now,
                    CreatedBy = "system"
                };

                var insertResult = await _bomItemRepository.InsertAsync(bomItem);
                if (!insertResult)
                {
                    return ApiResult.Fail("保存BOM明细失败", ResultCode.Error);
                }

                return ApiResult.Success(ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult.Fail($"添加BOM明细失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 批量为BOM添加物料明细
        /// </summary>
        /// <param name="bomId">BOM ID</param>
        /// <param name="itemDtos">物料明细数据列表</param>
        /// <returns>操作结果</returns>
        public async Task<ApiResult> BatchAddBomItemsAsync(Guid bomId, List<BomItemDto> itemDtos)
        {
            try
            {
                if (itemDtos == null || !itemDtos.Any())
                {
                    return ApiResult.Fail("明细列表不能为空", ResultCode.ValidationError);
                }

                // 验证BOM是否存在
                var bom = await _bomRepository.GetByIdAsync(bomId);
                if (bom == null)
                {
                    return ApiResult.Fail("BOM不存在", ResultCode.NotFound);
                }

                // 验证物料是否都存在
                var materialIds = itemDtos.Select(x => x.MaterialId).Distinct().ToList();
                var materials = await _dbContext.Db.Queryable<MaterialEntity>()
                    .Where(m => materialIds.Contains(m.Id))
                    .ToListAsync();

                if (materials.Count != materialIds.Count)
                {
                    return ApiResult.Fail("存在不存在的物料", ResultCode.NotFound);
                }

                // 检查重复
                var existingItems = await _dbContext.Db.Queryable<BomItem>()
                    .Where(bi => bi.BomId == bomId && materialIds.Contains(bi.MaterialId))
                    .ToListAsync();

                if (existingItems.Any())
                {
                    return ApiResult.Fail("存在已添加的物料", ResultCode.AlreadyExists);
                }

                // 创建BOM明细列表
                var bomItems = itemDtos.Select(dto => new BomItem
                {
                    Id = Guid.NewGuid(),
                    BomId = bomId,
                    MaterialId = dto.MaterialId,
                    ParentItemId = dto.ParentItemId,
                    Quantity = dto.Quantity,
                    LossRate = dto.LossRate,
                    InOutType = dto.InOutType,
                    Unit = dto.Unit?.Trim(),
                    Remark = dto.Remark?.Trim(),
                    CreatedAt = DateTime.Now,
                    CreatedBy = "system"
                }).ToList();

                var insertResult = await _bomItemRepository.InsertRangeAsync(bomItems);
                if (!insertResult)
                {
                    return ApiResult.Fail("批量保存BOM明细失败", ResultCode.Error);
                }

                return ApiResult.Success(ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult.Fail($"批量添加BOM明细失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 分页获取BOM物料明细列表
        /// </summary>
        /// <param name="bomId">BOM ID</param>
        /// <param name="inOutType">投入产出类型过滤</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页大小</param>
        /// <returns>分页的BOM物料明细列表</returns>
        public async Task<ApiResult<PageResult<List<BomItemListDto>>>> GetBomItemsPagedAsync(
            Guid bomId, MaterialRelationType? inOutType, int pageIndex, int pageSize)
        {
            try
            {
                // 参数验证
                if (pageIndex < 1)
                {
                    return ApiResult<PageResult<List<BomItemListDto>>>.Fail(
                        "页码必须大于0", ResultCode.ValidationError);
                }

                if (pageSize < 1 || pageSize > 1000)
                {
                    return ApiResult<PageResult<List<BomItemListDto>>>.Fail(
                        "每页大小必须在1-1000之间", ResultCode.ValidationError);
                }

                // 构建查询
                var query = _dbContext.Db.Queryable<BomItem>()
                    .Where(bi => bi.BomId == bomId);

                // 添加类型过滤
                if (inOutType.HasValue)
                {
                    query = query.Where(bi => bi.InOutType == inOutType.Value);
                }

                // 获取总数
                var totalCount = await query.CountAsync();

                // 分页查询并关联物料信息
                var data = await query
                    .LeftJoin<MaterialEntity>((bi, m) => bi.MaterialId == m.Id)
                    .Select((bi, m) => new BomItemListDto
                    {
                        Id = bi.Id,
                        BomId = bi.BomId,
                        MaterialId = bi.MaterialId,
                        MaterialNumber = m.MaterialNumber ?? string.Empty,
                        MaterialName = m.MaterialName ?? string.Empty,
                        ParentItemId = bi.ParentItemId,
                        Quantity = bi.Quantity,
                        LossRate = bi.LossRate,
                        InOutType = bi.InOutType,
                        InOutTypeName = GetInOutTypeName(bi.InOutType),
                        Unit = bi.Unit,
                        Remark = bi.Remark,
                        CreatedAt = bi.CreatedAt
                    })
                    .OrderBy(dto => dto.InOutType)
                    .OrderBy(dto => dto.MaterialNumber)
                    .Skip((pageIndex - 1) * pageSize)
                    .Take(pageSize)
                    .ToListAsync();

                var pageResult = new PageResult<List<BomItemListDto>>
                {
                    Data = data,
                    TotalCount = totalCount,
                    TotalPage = (int)Math.Ceiling(totalCount * 1.0 / pageSize)
                };

                return ApiResult<PageResult<List<BomItemListDto>>>.Success(pageResult, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<PageResult<List<BomItemListDto>>>.Fail(
                    $"获取BOM明细列表失败: {ex.Message}", ResultCode.Error);
            }
        }

        #endregion

        #region 私有辅助方法

        /// <summary>
        /// 验证BOM DTO的数据有效性
        /// </summary>
        /// <param name="dto">BOM数据传输对象</param>
        /// <returns>验证结果</returns>
        private async Task<ApiResult> ValidateBomDto(BomDto dto)
        {
            // 使用数据注解进行基础验证
            var validationResults = new List<ValidationResult>();
            var validationContext = new ValidationContext(dto);

            if (!Validator.TryValidateObject(dto, validationContext, validationResults, true))
            {
                var errors = string.Join("; ", validationResults.Select(vr => vr.ErrorMessage));
                return ApiResult.Fail($"数据验证失败: {errors}", ResultCode.ValidationError);
            }

            // 业务规则验证
            if (string.IsNullOrWhiteSpace(dto.BomNumber))
            {
                return ApiResult.Fail("BOM编号不能为空", ResultCode.ValidationError);
            }

            if (string.IsNullOrWhiteSpace(dto.ProductName))
            {
                return ApiResult.Fail("产品名称不能为空", ResultCode.ValidationError);
            }

            if (dto.ProductId == Guid.Empty)
            {
                return ApiResult.Fail("产品ID不能为空", ResultCode.ValidationError);
            }

            if (dto.DailyOutput <= 0)
            {
                return ApiResult.Fail("日产量必须大于0", ResultCode.ValidationError);
            }

            if (dto.ProcessRouteId == Guid.Empty)
            {
                return ApiResult.Fail("工艺路线ID不能为空", ResultCode.ValidationError);
            }

            return ApiResult.Success(ResultCode.Success);
        }

        /// <summary>
        /// 获取投入产出类型的中文名称
        /// </summary>
        /// <param name="inOutType">投入产出类型</param>
        /// <returns>中文名称</returns>
        private static string GetInOutTypeName(MaterialRelationType inOutType)
        {
            return inOutType switch
            {
                MaterialRelationType.Input => "投入",
                MaterialRelationType.Output => "产出",
                MaterialRelationType.ByProduct => "副产品",
                MaterialRelationType.Scrap => "废料",
                MaterialRelationType.Recycled => "可回收",
                _ => "未知"
            };
        }

        /// <summary>
        /// 为 MES 工具服务提供的异步方法别名 - 获取BOM树形结构
        /// </summary>
        /// <param name="productId">产品ID</param>
        /// <returns></returns>
        public async Task<object> GetBomTreeAsync(long productId)
        {
            try
            {
                // 模拟BOM树形结构数据
                // 在实际实现中，这里应该根据productId查询真实的BOM数据
                return new
                {
                    productId = productId,
                    productName = $"产品_{productId}",
                    bomTree = new[]
                    {
                        new
                        {
                            id = 1,
                            name = "主要组件",
                            children = new[]
                            {
                                new { id = 11, name = "子组件1", quantity = 2, unit = "个" },
                                new { id = 12, name = "子组件2", quantity = 1, unit = "个" }
                            }
                        },
                        new
                        {
                            id = 2,
                            name = "辅助组件",
                            children = new[]
                            {
                                new { id = 21, name = "辅助件1", quantity = 4, unit = "个" },
                                new { id = 22, name = "辅助件2", quantity = 2, unit = "个" }
                            }
                        }
                    },
                    totalComponents = 6,
                    generatedAt = DateTime.UtcNow
                };
            }
            catch (Exception ex)
            {
                return new
                {
                    success = false,
                    message = $"获取BOM树形结构失败: {ex.Message}",
                    productId = productId
                };
            }
        }

        #endregion
    }
}